package co.fitstart.admin.service.impl;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.demo2do.core.BusinessException;
import com.demo2do.core.persistence.annotation.Dynamic;
import com.demo2do.core.support.Page;
import com.demo2do.core.utils.CollectionUtils;
import com.demo2do.core.utils.DateFormatUtils;
import com.demo2do.core.utils.DateUtils;

import co.fitstart.admin.dao.CoachDao;
import co.fitstart.admin.dao.CoachRestCalendarDao;
import co.fitstart.admin.dao.CoachSettlementDao;
import co.fitstart.admin.dao.CourseDaoSupport;
import co.fitstart.admin.dao.CourseRoundResultDao;
import co.fitstart.admin.dao.CourseScheduleDao;
import co.fitstart.admin.dao.InbodyRecordDao;
import co.fitstart.admin.dao.ReportDao;
import co.fitstart.admin.dao.ScheduleDaoSupport;
import co.fitstart.admin.dao.StudentDao;
import co.fitstart.admin.entity.content.ExpiryScheduleSearch;
import co.fitstart.admin.entity.report.CoachFinishedSchedule;
import co.fitstart.admin.entity.security.Login;
import co.fitstart.admin.handler.TencentHandler;
import co.fitstart.admin.service.CourseScheduleService;
import co.fitstart.entity.Constant;
import co.fitstart.entity.coach.CoachRestCalendar;
import co.fitstart.entity.coach.CoachRestType;
import co.fitstart.entity.coach.CoachSigninRecord;
import co.fitstart.entity.coach.CoachSigninStatistic;
import co.fitstart.entity.coach.PrincipalReportRelation;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.CourseUnit;
import co.fitstart.entity.course.round.CourseRound;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.course.schedule.CourseScheduleAttribute;
import co.fitstart.entity.course.schedule.ScheduleStatus;
import co.fitstart.entity.course.schedule.SigninRecord;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.order.OrderStatus;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.principal.PrincipalMessage;
import co.fitstart.entity.report.FinishedCourseReport;
import co.fitstart.entity.report.VisitorReport;
import co.fitstart.entity.site.RoomScheduleRecord;
import co.fitstart.entity.site.RoomType;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.Student;

/**
 * 
 * @author mohanlan
 *
 */
@Service("courseScheduleService")
public class CourseScueduleServiceImpl extends DataServiceImpl<CourseSchedule> implements CourseScheduleService {

    @Dynamic
    private CourseScheduleDao courseScheduleDao;

    @Dynamic
    private CoachSettlementDao coachSettlementDao;

    @Dynamic
    private ReportDao reportDao;

    @Dynamic
    private StudentDao studentDao;

    @Dynamic
    private CoachRestCalendarDao coachRestCalendarDao;

    @Dynamic
    private CoachDao coachDao;

    @Autowired
    private CourseDaoSupport courseDaoSupport;

    @Autowired
    private ScheduleDaoSupport scheduleDaoSupport;

    @Autowired
    private TencentHandler tencentHandler;

    @Dynamic
    private CourseRoundResultDao courseRoundResultDao;

    @Dynamic
    private InbodyRecordDao inbodyRecordDao;
    
    
    private static final Logger logger = LoggerFactory.getLogger(CourseScueduleServiceImpl.class);

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#list(co.fitstart.entity.
     * site.Site, co.fitstart.entity.course.schedule.ScheduleStatus,
     * java.util.List)
     */
    public List<CourseSchedule> list(Site site, ScheduleStatus scheduleStatus, List<Date> dates) {

        List<CourseSchedule> result = new ArrayList<CourseSchedule>();

        if (site == null) {
            result = courseScheduleDao.listCourseSchedule(scheduleStatus, dates);
        } else {
            result = courseScheduleDao.listCourseSchedule(site, scheduleStatus, dates);
        }

        return result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#list(java.util.List, co.fitstart.entity.course.schedule.ScheduleStatus, java.util.List)
     */
    public List<CourseSchedule> list(List<Site> sites, ScheduleStatus scheduleStatus, List<Date> dates) {
        return courseScheduleDao.listCourseSchedule(sites, scheduleStatus, dates);
    }

    /*
     * (non-Javadoc)
     * 
     * @see co.fitstart.admin.service.ecommerce.CourseScheduleService#list(co.
     * fitstart.common.entity.site.Site)
     */
    public Map<String, List<CourseSchedule>> list(Site site) {

        List<CourseSchedule> courseSchedules = courseScheduleDao.listCourseSchedule(site);

        return CollectionUtils.constructMapValuesFromList(courseSchedules, "courseRound.id");

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.course.OfflineRoundService#listCourseSchedule(
     * co.fitstart.entity.site.Site, co.fitstart.entity.principal.Principal,
     * java.util.Date, java.util.Date)
     */
    public List<CourseSchedule> listCourseSchedule(Site site, Principal coach, Date beginDate, Date endDate) {
        return courseDaoSupport.listCourseSchedule(site, coach, beginDate, endDate);
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#listCourseSchedule(java.util.List, co.fitstart.entity.principal.Principal, java.util.Date, java.util.Date)
     */
    public List<CourseSchedule> listCourseSchedule(List<Site> sites, Principal coach, Date beginDate, Date endDate) {
        return courseDaoSupport.listCourseSchedule(sites, coach, beginDate, endDate);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.course.OfflineRoundService#listCourseSchedule(
     * java.util.List, java.lang.String)
     */
    public List<CourseSchedule> listCourseSchedule(List<CourseRound> courseRounds, String dayhour) {
        String[] dayhours = StringUtils.split(dayhour, "|");

        return courseScheduleDao.listCourseSchedule(courseRounds, DateUtils.toDate(dayhours[0]), dayhours[1]);
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#listFinishedSchedule(co.
     * fitstart.entity.site.Site, java.util.Date, java.lang.String,
     * com.demo2do.core.support.Page)
     */
    public List<CourseSchedule> listFinishedSchedule(Site site, Date date, String period, Page page) {
        // 1. get startTime and endTime by date and period
        Map<String, Date> times = this.getTimes(date, period);

        List<CourseSchedule> result = new ArrayList<CourseSchedule>();
        if (site == null) {
            result = courseScheduleDao.listCourseSchedule(CourseType.getOffline(), ScheduleStatus.FINISH,
                    times.get("startTime"), times.get("endTime"), page.getBeginIndex(), page.getMaxResultRecords());
        } else {
            result = courseScheduleDao.listCourseSchedule(CourseType.getOffline(), site, ScheduleStatus.FINISH,
                    times.get("startTime"), times.get("endTime"), page.getBeginIndex(), page.getMaxResultRecords());
        }
        page.calculate(result.size());

        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#listFinishedSchedule(java.util.List, java.util.Date, java.lang.String, com.demo2do.core.support.Page)
     */
    public List<CourseSchedule> listFinishedSchedule(List<Site> sites, Date date, String period, Page page) {
        Map<String, Date> times = this.getTimes(date, period);

        List<CourseSchedule> result = courseScheduleDao.listCourseSchedule(CourseType.getOffline(), sites, ScheduleStatus.FINISH,
                    times.get("startTime"), times.get("endTime"), page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());

        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#listCoachFinishedSchedule
     * (co.fitstart.entity.site.Site, java.util.Date, java.lang.String,
     * com.demo2do.core.support.Page)
     */
    public List<CoachFinishedSchedule> listCoachFinishedSchedule(Site site, Date date, String period, Page page) {
        // 1. get startTime and endTime by date and period
        Map<String, Date> times = this.getTimes(date, period);

        List<CoachFinishedSchedule> result = new ArrayList<CoachFinishedSchedule>();
        if (site == null) {
            result = courseScheduleDao.listCoachFinishSchedule(CourseType.getOffline(), ScheduleStatus.FINISH,
                    times.get("startTime"), times.get("endTime"), page.getBeginIndex(), page.getMaxResultRecords());
        } else {
            result = courseScheduleDao.listCoachFinishSchedule(site, CourseType.getOffline(), ScheduleStatus.FINISH,
                    times.get("startTime"), times.get("endTime"), page.getBeginIndex(), page.getMaxResultRecords());
        }
        page.calculate(result.size());

        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#listCoachFinishedSchedule(java.util.List, java.util.Date, java.lang.String, com.demo2do.core.support.Page)
     */
    public List<CoachFinishedSchedule> listCoachFinishedSchedule(List<Site> sites, Date date, String period, Page page) {
        Map<String, Date> times = this.getTimes(date, period);

        List<CoachFinishedSchedule> result = courseScheduleDao.listCoachFinishSchedule(sites, CourseType.getOffline(), ScheduleStatus.FINISH,
                    times.get("startTime"), times.get("endTime"), page.getBeginIndex(), page.getMaxResultRecords());
        page.calculate(result.size());

        return result.size() > page.getEveryPage() ? result.subList(0, page.getEveryPage()) : result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#listExperienceSchedule(co
     * .fitstart.entity.principal.Principal, java.util.Date)
     */
    public List<CourseSchedule> listExperienceSchedule(Principal coach, Date day) {
        String currentHour = DateFormatUtils.format(new Date(), "HH:mm");
        Date today = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd");
        if (today.equals(day)) {
            return courseScheduleDao.listExperienceSchedule(coach, day, currentHour);
        } else {
            return courseScheduleDao.listExperienceSchedule(coach, day);
        }
    }

    /**
     * 
     * @param date
     * @param period
     * @return
     */
    private Map<String, Date> getTimes(Date date, String period) {

        Map<String, Date> times = new HashMap<String, Date>();

        if (StringUtils.equalsIgnoreCase(period, "day")) {
            times.put("startTime", date);
            times.put("endTime", date);
        } else if (StringUtils.equalsIgnoreCase(period, "week")) {
            times.put("startTime", DateUtils.getFirstWeekDay(date));
            times.put("endTime", DateUtils.getLastWeekDay(date));
        } else if (StringUtils.equalsIgnoreCase(period, "month")) {
            times.put("startTime", DateUtils.getFirstMonthDay(date));
            times.put("endTime", DateUtils.getLastMonthDay(date));
        }

        return times;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.ecommerce.CourseScheduleService#changeTime(co.
     * fitstart.common.entity.course.CourseSchedule)
     */
    public void changeTime(CourseSchedule courseSchedule) {

        genericDaoSupport.update(courseSchedule);

        PrincipalMessage principalMessage = new PrincipalMessage(courseSchedule.getCoach());
        genericDaoSupport.save(principalMessage.initialize("单节课程改课时间", "您有新的单节课程时间", "您有新的单节课程时间已被修改，学员为【"
                + courseSchedule.getStudent().getName() + "】，上课时间为【" + courseSchedule.getDayhour() + "】,请在我的学员点击查看。"));
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.ecommerce.CourseScheduleService#changeCoach(co.
     * fitstart.common.entity.course.CourseSchedule,
     * co.fitstart.common.entity.system.Principal)
     */
    public void changeCoach(CourseSchedule courseSchedule, Principal coach) {

        genericDaoSupport.update(courseSchedule.changeCoach(coach));

        String message = "您有新的单节课程，学员为【" + courseSchedule.getStudent().getName() + "】，上课时间为【"
                + courseSchedule.getDayhour() + "】,请在我的学员点击查看，并及时上课。";

        PrincipalMessage principalMessage = new PrincipalMessage(courseSchedule.getCoach());
        genericDaoSupport.save(principalMessage.initialize("新单节课程", "您有新的单节课程", message));

        // send tencent message to coach
        tencentHandler.sendMessage(message, coach.getMobile());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#editSchedule(co.fitstart.
     * entity.course.schedule.CourseSchedule,
     * co.fitstart.entity.principal.Principal, co.fitstart.entity.site.Site,
     * java.util.Date, java.lang.String)
     */
    public void editSchedule(CourseSchedule courseSchedule, Principal coach, Site site, Date day, String hour) {
        RoomType roomType = this.getAvailableRoomType(site, day, hour, courseSchedule.getCourse().isExperience(), courseSchedule);
        // edit room schedule record
        RoomScheduleRecord roomScheduleRecord = courseSchedule.getRoomSchedule();
        if (roomScheduleRecord != null) {
            genericDaoSupport.update(roomScheduleRecord.updateTime(day, hour, site, roomType));
        } else {
            String roundCode = courseSchedule.getCourseRound().getCode();
            roomScheduleRecord = new RoomScheduleRecord(site, roundCode, day, hour, roomType, 1);
            genericDaoSupport.save(roomScheduleRecord);
        }
        
        // if individual experience, update coachRestCalendar
        if((courseSchedule.getCourseRound().getCourseType().isIndividual() || courseSchedule.getCourseRound().getCourseType().isSpecial() || courseSchedule.getCourseRound().getCourseType().isPosture()) && courseSchedule.getCourse().isExperience()) {
            Date dayhour = DateUtils.parseDate(DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM-dd ") + courseSchedule.getOccurHour(), "yyyy-MM-dd HH:mm");
            String restHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(dayhour, 1), "HH:mm");
            CoachRestCalendar calendar = courseScheduleDao.loadCoachRestCalendar(courseSchedule.getCoach(), courseSchedule.getOccurDate(), restHour);
            
            if(calendar == null || calendar.isDuty()) {
                Date newDayHour = DateUtils.parseDate(DateFormatUtils.format(day, "yyyy-MM-dd ") + hour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                
                CoachRestCalendar newCalendar = new CoachRestCalendar(coach, CoachRestType.EXPERIENCE, day, newRestHour, true);
                genericDaoSupport.save(newCalendar);
            } else if(calendar != null && calendar.isExperience()) {
                Date newDayHour = DateUtils.parseDate(DateFormatUtils.format(day, "yyyy-MM-dd ") + hour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                
                genericDaoSupport.update(calendar.edit(coach, day, newRestHour));
            } else {
                throw new BusinessException("所选时间不可预约");
            }
        }
        
        if(courseSchedule.getCourseRound().getCourseType().isSpecial() && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_UNION)) {
        	Date dayhour = DateUtils.parseDate(DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM-dd ") + courseSchedule.getOccurHour(), "yyyy-MM-dd HH:mm");
            String restHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(dayhour, 1), "HH:mm");
            CoachRestCalendar calendar = courseScheduleDao.loadCoachRestCalendar(courseSchedule.getCoach(), courseSchedule.getOccurDate(), restHour);
            
            if(calendar == null || calendar.isDuty()) {
                Date newDayHour = DateUtils.parseDate(DateFormatUtils.format(day, "yyyy-MM-dd ") + hour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                CoachRestCalendar newCalendar = new CoachRestCalendar(coach, CoachRestType.ANNUAL_LEAVE, day, newRestHour, true);
                genericDaoSupport.save(newCalendar);
                
            } else if(calendar != null && calendar.getCoachRestType().equals(CoachRestType.ANNUAL_LEAVE)) {
                Date newDayHour = DateUtils.parseDate(DateFormatUtils.format(day, "yyyy-MM-dd ") + hour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                genericDaoSupport.update(calendar.edit(coach, day, newRestHour));
            } else {
                throw new BusinessException("所选时间不可预约");
            }
        }
        
        scheduleDaoSupport.disableScheduleSmsNotice(courseSchedule);

        genericDaoSupport.update(courseSchedule.editCoach(coach).editTime(day, hour).editSite(site)
                .roomScheduleRecord(roomScheduleRecord));

        String message = "您有新的单节课程，学员为【" + courseSchedule.getStudent().getName() + "】，上课时间为【"
                + courseSchedule.getDayhour() + "】,请在我的学员点击查看，并及时上课。";

        PrincipalMessage principalMessage = new PrincipalMessage(courseSchedule.getCoach());
        genericDaoSupport.save(principalMessage.initialize("新单节课程", "您有新的单节课程", message));

        // send tencent message to coach
        tencentHandler.sendMessage(message, coach.getMobile());
        
        scheduleDaoSupport.createScheduleSmsNotice(courseSchedule);

        scheduleDaoSupport.recalculateStudentCourseware(courseSchedule.getCourseRound());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.ecommerce.CourseScheduleService#addSchedule(co.
     * fitstart.common.entity.course.CourseRound, int)
     */
    public void addSchedule(CourseRound courseRound, int amount) {

        // Step1 create courseSchedule by courseRound & amount
        for (int i = 0; i < amount; i++) {
            CourseSchedule courseSchedule = new CourseSchedule(courseRound, ScheduleStatus.SCHEDULE);
            genericDaoSupport.save(courseSchedule);
        }

        // Step2 update course round's course_amount attribute
        if (MapUtils.isEmpty(courseRound.getAttrs()) || courseRound.getAttrs().get(Constant.COURSE_AMOUNT) == null) {
            genericDaoSupport.update(
                    courseRound.addAttribute(Constant.COURSE_AMOUNT, courseRound.getCourse().getHours() + amount));
        } else {
            genericDaoSupport.update(courseRound.editAttribute(Constant.COURSE_AMOUNT,
                    Integer.parseInt(courseRound.getAttrs().get(Constant.COURSE_AMOUNT).getValue()) + amount));
        }

        // Step3 create new principal message
        PrincipalMessage principalMessage = new PrincipalMessage(courseRound.getCoach());
        genericDaoSupport.save(principalMessage.initialize("新增课程数", "您有课程新增" + amount + "节课",
                "您有课程新增" + amount + "节课，学员为【" + courseRound.getStudent().getName() + "】请在我的学员点击查看具体情况。"));

    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.course.CourseScheduleService#addSchedule(co.
     * fitstart.entity.course.round.CourseRound,
     * co.fitstart.entity.course.schedule.CourseSchedule,
     * co.fitstart.entity.site.RoomType)
     */
    public void addSchedule(CourseRound courseRound, CourseSchedule courseSchedule, RoomType roomType) {
        // 1. validate site remain
        List<RoomScheduleRecord> roomScheduleRecords = courseScheduleDao.getRoomRemain(courseSchedule.getSite(),
                roomType, courseSchedule.getOccurDate(), courseSchedule.getOccurHour());
        int siteRemain = roomType.isCommon() ? courseSchedule.getSite().getRoomLimit().getCommonLimit()
                : courseSchedule.getSite().getRoomLimit().getRoomLimit();
        for (RoomScheduleRecord roomScheduleRecord : roomScheduleRecords) {
            siteRemain = siteRemain - roomScheduleRecord.getAmount();
        }
        if (siteRemain <= 0) {
            throw new BusinessException("所选条件，房间已满");
        }

        // 2. validate same date & time course schedule
        CourseSchedule same = courseScheduleDao.getCourseSchedule(courseRound.getStudent(),
                courseSchedule.getOccurDate(), courseSchedule.getOccurHour());
        if (same != null) {
            throw new BusinessException("所选时间您已有其他课程");
        }

        // 3. validate same day
        List<CourseSchedule> sameDay = courseScheduleDao.listCourseSchedule(courseRound.getStudent(), courseRound,
                courseSchedule.getOccurDate());
        if (org.apache.commons.collections.CollectionUtils.isNotEmpty(sameDay)) {
            throw new BusinessException("本课程今日已有预约");
        }

        // 4. validate student expiry times
        if (courseRound.isDishonesty()) {
            // put monday and sunday as parameter
            Date startDate = DateUtils.getFirstWeekDay(new Date());
            Date endDate = DateUtils.addDays(startDate, 6);

            List<CourseSchedule> thisWeekSchedules = courseScheduleDao.listCourseSchedule(courseSchedule.getStudent(),
                    courseRound, startDate, endDate);
            if (org.apache.commons.collections.CollectionUtils.isNotEmpty(thisWeekSchedules)) {
                throw new BusinessException("失信用户，一周只能预约一节课程");
            }
        }

        // save new site schedule record
        RoomScheduleRecord roomScheduleRecord = new RoomScheduleRecord(courseSchedule.getSite(), courseRound.getCode(),
                courseSchedule.getOccurDate(), courseSchedule.getOccurHour(), roomType, 1);
        genericDaoSupport.save(roomScheduleRecord);

        // save new site
        genericDaoSupport.save(courseSchedule.addYearSchedule(courseRound));
    }

    /*
     * (non-Javadoc)
     * 
     * @see co.fitstart.admin.service.ecommerce.CourseScheduleService#
     * calculateDayhour(co.fitstart.common.entity.course.CourseRound, int,
     * java.util.Date, java.lang.String[])
     */
    public List<String> calculateDayhour(CourseRound courseRound, int amount, Date beginDate, String... dayhours) {

        List<String> result = new ArrayList<String>();

        // prepare monday and offset weeks
        Date monday = DateUtils.getFirstWeekDay(beginDate);
        int divisor = courseRound.getCourse().getHours() / courseRound.getCourse().getWeeks();

        int weeks = amount / divisor;
        int rest = amount % divisor;

        if (ArrayUtils.isNotEmpty(dayhours)) {

            for (int j = 0; j < dayhours.length; j++) { // for selected dayhours

                String daySlashHour = dayhours[j];
                int currentWeeks = weeks;

                if (rest <= j && rest > 0) {
                    currentWeeks = currentWeeks + 1;
                }

                String[] dayhour = StringUtils.split(daySlashHour, "|");
                int offset = Integer.valueOf(dayhour[0]);

                // calculate first day
                Date first = DateUtils.addDays(monday, offset);

                // init index & weeks
                int index = first.before(beginDate) ? 1 : 0;
                int tempWeeks = first.before(beginDate) ? currentWeeks + 1 : currentWeeks;

                for (int i = index; i < tempWeeks; i++) { // for every weeks

                    Date base = DateUtils.addWeeks(monday, i); // calculate base
                                                               // first
                    Date day = DateUtils.addDays(base, offset); // calculate
                                                                // selected day
                                                                // of every week

                    result.add(DateFormatUtils.format(day, "yyyy-MM-dd") + "|" + dayhour[1]);
                }

            }

        }

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#finish(co.fitstart.admin.
     * entity.security.Login, co.fitstart.entity.principal.Principal,
     * co.fitstart.entity.course.schedule.CourseSchedule)
     */
    public void finish(Login login, Principal coach, CourseSchedule courseSchedule) {
        // Step1: check dishonesty
        CourseRound courseRound = courseSchedule.getCourseRound();
        if (courseRound != null && courseRound.isDishonesty()) {
            List<ScheduleStatus> scheduleStatus = new ArrayList<>();
            scheduleStatus.add(ScheduleStatus.EXPIRY);

            List<CourseSchedule> expirySchedules = courseScheduleDao.listCourseSchedule(courseRound, scheduleStatus, 0, 3);
            if (expirySchedules == null || expirySchedules.size() <= 2) {
                genericDaoSupport.update(courseRound.editAttribute("dishonesty", "0"));
            } else {
                scheduleStatus.add(ScheduleStatus.FINISH);

                List<CourseSchedule> schedules = courseScheduleDao.listCourseSchedule(courseRound, scheduleStatus, 0, 2);
                if (schedules == null || schedules.size() < 2
                        || (schedules.get(0).getScheduleStatus() == ScheduleStatus.FINISH
                                && schedules.get(1).getScheduleStatus() == ScheduleStatus.FINISH)
                        || (schedules.get(0).getScheduleStatus() == ScheduleStatus.FINISH
                                && schedules.get(1) == courseSchedule)
                        || (schedules.get(0) == courseSchedule
                                && schedules.get(1).getScheduleStatus() == ScheduleStatus.FINISH)) {
                    genericDaoSupport.update(courseRound.editAttribute("dishonesty", "0"));
                }
            }
        }

        // Step2 sign in for course schedule
        genericDaoSupport.update(courseSchedule.changeStatus(ScheduleStatus.FINISH));

        // Step3 create courseSchedule signin attribute
        this.createCourseScheduleAttr(courseSchedule, login);

        // Step4 update course round's finished count & status
        genericDaoSupport.update(courseRound.signin());
        
        if(courseSchedule.getCourseType().isIndividual() || courseSchedule.getCourseType().isSpecial() || courseSchedule.getCourseType().isPosture()) {
            int courseAmount = courseRound.getCourseAmount();
            
            int finished = courseScheduleDao.count(courseRound, ScheduleStatus.FINISH);
            
            if(courseAmount == finished) {
                genericDaoSupport.update(courseRound.finishCourseRound());
            }
        }

        // Step5 update order status when course round finished
        if (courseRound.isFinish()) {

            if (MapUtils.isNotEmpty(courseRound.getAttrs())
                    && courseRound.getAttrs().get(co.fitstart.entity.Constant.COURSER_ROUND_ORDER) != null) {

                Long orderId = Long.parseLong(
                        courseRound.getAttrs().get(co.fitstart.entity.Constant.COURSER_ROUND_ORDER).getValue());
                Order order = genericDaoSupport.load(Order.class, orderId);
                order.orderStatus(OrderStatus.COMPLETE);
                genericDaoSupport.update(order);
            }
        }

        // Step6: save finished course report if courseType is individual or year
        if (courseSchedule.getCourseType().isIndividual() || courseSchedule.getCourseType().isSpecial() || courseSchedule.getCourseType().isPosture() || courseRound.getCourseType().isYear()) {
            this.createFinishedCourseReport(courseRound, courseSchedule);

            // 6.1 if the course is experience, save or update visitor report
            if (courseRound.getCourse().isExperience()) {
                this.createVisitorReport(courseRound.getSite(), courseSchedule.getOccurDate());
            }
        }

        // Step7: save signin record
        this.createSigninRecord(courseSchedule);

        // Step8: save coach signin record and update coach signin statistic
        this.createCoachSigninRecord(courseSchedule, login.getName());
        
        // Step9: record first&last bwh&inbody
        if(!courseRound.getCourse().isExperience()) {
            courseDaoSupport.recordCourseRoundResult(courseRound);
        }
    }

    /**
     * 
     * @param courseSchedule
     * @param coach
     */
    private void createCourseScheduleAttr(CourseSchedule courseSchedule, Login login) {

        // create signinPersonName attribute
        CourseScheduleAttribute signinPersonName = new CourseScheduleAttribute(courseSchedule,
                Constant.SIGNIN_PERSON_NAME, login.getName());
        genericDaoSupport.save(signinPersonName);

        // create signinPersonId attribute
        CourseScheduleAttribute signPersonId = new CourseScheduleAttribute(courseSchedule, Constant.SIGN_PERSON_ID,
                login.getId().toString());
        genericDaoSupport.save(signPersonId);

        // create signinTime attribute
        CourseScheduleAttribute signinTime = new CourseScheduleAttribute(courseSchedule, Constant.SIGNIN_TIME,
                DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"));
        genericDaoSupport.save(signinTime);
    }

    /**
     * 
     * @param courseRound
     * @param courseSchedule
     */
    private void createFinishedCourseReport(CourseRound courseRound, CourseSchedule courseSchedule) {
        Date occurTime = DateUtils.parseDate(DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM-dd") + " "
                + courseSchedule.getOccurHour() + ":00", "yyyy-MM-dd HH:mm:ss");
        FinishedCourseReport report = new FinishedCourseReport(courseSchedule, occurTime,
                this.calculateOffer(courseRound, courseSchedule));
        genericDaoSupport.save(report);
    }

    /**
     * 
     * @param site
     * @param occurDate
     */
    private void createVisitorReport(Site site, Date occurDate) {
        VisitorReport visitorReport = reportDao.loadVisitorReport(site, occurDate);

        if (visitorReport == null) {
            VisitorReport report = new VisitorReport();
            genericDaoSupport.save(report.initilization(site, 1, 0, 0, occurDate));
        } else {
            long experienceAmount = visitorReport.getExperienceAmount() + 1;
            genericDaoSupport.update(visitorReport.editExperienceAmount(experienceAmount));
        }
    }

    /**
     * 
     * @param courseRound
     * @param courseSchedule
     * @return
     */
    private int calculateOffer(CourseRound courseRound, CourseSchedule courseSchedule) {
        int offer = 0, price = courseRound.getOrder().getPrice();

        // calculate individual course offer
        if (courseRound.getCourseType().isIndividual() || courseRound.getCourseType().isSpecial() || courseRound.getCourseType().isSpecial()) {

            if (!MapUtils.isEmpty(courseRound.getAttrs())) {
                offer = price / Integer.parseInt(courseRound.getAttrs().get(Constant.COURSE_AMOUNT).getValue());
            } else {
                offer = price / courseRound.getCourse().getHours();
            }

        } else if (courseRound.getCourseType().isYear()) { // calculate year
                                                           // course offer

            Date occurTime = DateUtils.parseDate(DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM-dd")
                    + " " + courseSchedule.getOccurHour() + ":00", "yyyy-MM-dd HH:mm:ss");
            List<CourseSchedule> courseSchedules = courseScheduleDao.listCourseSchedule(courseRound,
                    courseSchedule.getOccurDate(), courseSchedule.getOccurHour());
            if (courseSchedules.isEmpty()) {

                offer = new BigDecimal(occurTime.getTime() - courseRound.getOrder().getPayTime().getTime())
                        .divide(new BigDecimal(courseRound.getRoundTimes()), 5, RoundingMode.CEILING)
                        .multiply(new BigDecimal(price)).intValue();
            } else {
                CourseSchedule lastSchedule = courseSchedules.get(0);
                Date lastOccurTime = DateUtils
                        .parseDate(DateFormatUtils.format(lastSchedule.getOccurDate(), "yyyy-MM-dd") + " "
                                + lastSchedule.getOccurHour() + ":00", "yyyy-MM-dd HH:mm:ss");
                offer = new BigDecimal(occurTime.getTime() - lastOccurTime.getTime())
                        .divide(new BigDecimal(courseRound.getRoundTimes()), 5, RoundingMode.CEILING)
                        .multiply(new BigDecimal(price)).intValue();
            }
        }

        return offer;
    }

    /**
     * 
     * @param courseSchedule
     */
    private void createSigninRecord(CourseSchedule courseSchedule) {
        SigninRecord signinRecord = new SigninRecord(courseSchedule, false);
        genericDaoSupport.save(signinRecord);
    }

    /**
     * 
     * @param courseSchedule
     */
    private void createCoachSigninRecord(CourseSchedule courseSchedule, String signPerson) {
    	
    	// save coach signin record first
        CoachSigninRecord signinRecord = new CoachSigninRecord(courseSchedule, signPerson);
        genericDaoSupport.save(signinRecord);
        
        if(courseSchedule.getStudent().getUser().isOrdered()) {
        	
        	CoachSigninStatistic signinStatistic = coachDao.loadCoachSigninStatistic(courseSchedule.getCoach(), DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM"));
        	if (signinStatistic == null) {
        		signinStatistic = new CoachSigninStatistic(courseSchedule);
                signinStatistic.experienceHour(0);
                signinStatistic.giveHour(0);
                signinStatistic.hour(0);
        	}
        	
        }

        if(!courseSchedule.getCourse().isExperience() || courseSchedule.getStudent().getUser().isOrdered()) {
         // save or update this month has coach signin statistic
            CoachSigninStatistic signinStatistic = coachDao.loadCoachSigninStatistic(courseSchedule.getCoach(), DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM"));
            if (signinStatistic == null) {
                signinStatistic = new CoachSigninStatistic(courseSchedule);
                signinStatistic.experienceHour(0);
                signinStatistic.giveHour(0);
                if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_POSTURE)) {
                    signinStatistic.hour(2);
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_UNION)) {
                    signinStatistic.hour(1.5);
                // 常规体验课 1节。不计课时
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_EXPERIENCE)) {
                    signinStatistic.experienceHour(1.5);
                // 1V1体验周卡: 7天  2节  不计课时
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_WEEK_EXPERIENCE)) {
                    signinStatistic.experienceHour(1);
                // 专项体态管理周卡(体验)：1周，2节，课时计算2节，上了就计，不上不计
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_EXPERIENCE)) {
                    signinStatistic.experienceHour(1).hour(1);
                // 豪华14天课程包：14天，4节， 课时计算4节，上了就计，不上不计
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_WEEK_EXPERIENCE)) {
                    signinStatistic.experienceHour(1).hour(1);
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_EXPERIENCE_WEEK)) {
                    signinStatistic.experienceHour(1).hour(1);
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_EXPERIENCE_WEEK)) {
                    signinStatistic.experienceHour(1).hour(1);
                }  else if(courseSchedule.getCourse().isGive()) {
                    signinStatistic.giveHour(1);
                } else {
                    signinStatistic.hour(1);
                }
                genericDaoSupport.save(signinStatistic);
            } else {
            	
//            	1V1体验周卡: 7天  2节  不计课时
//            	专项体态管理周卡(体验)：1周，2节，课时计算2节，上了就计，不上不计
//            	常规体验课：1节。不计课时
//            	豪华14天课程包：14天，4节， 课时计算4节，上了就计，不上不计
            	
            	// 专项体态管理周卡(体验)：1周，2节，课时计算2节，上了就计，不上不计
                if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_POSTURE)) {
                    genericDaoSupport.update(signinStatistic.hour(2));
                }  else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_UNION)) {
                	genericDaoSupport.update(signinStatistic.hour(1.5));
                // 常规体验课 1节。不计课时
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_EXPERIENCE)) {
                    genericDaoSupport.update(signinStatistic.experienceHour(1.5));
                // 1V1体验周卡: 7天  2节  不计课时
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_WEEK_EXPERIENCE)) {
                    genericDaoSupport.update(signinStatistic.experienceHour(1));
                // 专项体态管理周卡(体验)：1周，2节，课时计算2节，上了就计，不上不计
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_EXPERIENCE)) {
                    genericDaoSupport.update(signinStatistic.experienceHour(1).hour(1));
                // 豪华14天课程包：14天，4节， 课时计算4节，上了就计，不上不计
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_WEEK_EXPERIENCE)) {
                    genericDaoSupport.update(signinStatistic.experienceHour(1).hour(1));
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.NORMAL_EXPERIENCE_WEEK)) {
                    genericDaoSupport.update(signinStatistic.experienceHour(1).hour(1));
                } else if(courseSchedule.getCourse().getCourseUnit() != null && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_EXPERIENCE_WEEK)) {
                    genericDaoSupport.update(signinStatistic.experienceHour(1).hour(1));
                }  else if(courseSchedule.getCourse().isGive()) {
                    genericDaoSupport.update(signinStatistic.giveHour(1));
                } else {
                    genericDaoSupport.update(signinStatistic.hour(1));
                }
            }
        }
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#rearrange(co.fitstart.entity.course.schedule.CourseSchedule, co.fitstart.entity.site.Site, co.fitstart.entity.principal.Principal, java.util.Date, java.lang.String)
     */
    public void rearrange(CourseSchedule courseSchedule, Site site, Principal coach, Date occurDate, String occurHour) {
        RoomType roomType = this.getAvailableRoomType(site, occurDate, occurHour, courseSchedule.getCourse().isExperience());
        // check dishonesty
        CourseRound courseRound = courseSchedule.getCourseRound();
        if (courseRound.isDishonesty()) {
            List<ScheduleStatus> scheduleStatus = new ArrayList<>();
            scheduleStatus.add(ScheduleStatus.EXPIRY);

            List<CourseSchedule> expirySchedules = courseScheduleDao.listCourseSchedule(courseRound, scheduleStatus, 0,
                    2);
            if (expirySchedules == null || expirySchedules.size() < 2) {
                genericDaoSupport.update(courseRound.editAttribute("dishonesty", "0"));
            } else {
                scheduleStatus.add(ScheduleStatus.FINISH);

                List<CourseSchedule> schedules = courseScheduleDao.listCourseSchedule(courseRound, scheduleStatus,
                        courseSchedule, 0, 2);
                if (schedules == null || schedules.size() < 2
                        || (schedules.get(0).getScheduleStatus() == ScheduleStatus.FINISH
                                && schedules.get(1).getScheduleStatus() == ScheduleStatus.FINISH)) {
                    genericDaoSupport.update(courseRound.editAttribute("dishonesty", "0"));
                }
            }
        }

        // update or save roomScheduleRecord
        RoomScheduleRecord roomScheduleRecord = courseSchedule.getRoomSchedule();
        if (roomScheduleRecord != null) {
            genericDaoSupport.update(roomScheduleRecord.updateTime(occurDate, occurHour, courseSchedule.getSite(), roomType));
        } else {
            String roundCode = courseSchedule.getCourseRound().getCode();
            roomScheduleRecord = new RoomScheduleRecord(courseSchedule.getSite(), roundCode, occurDate, occurHour,
                    roomType, 1);
            genericDaoSupport.save(roomScheduleRecord);
        }
        
        String day = DateFormatUtils.format(occurDate, "yyyy-MM-dd");
        
        // if individual experience, update coachRestCalendar
        if((courseSchedule.getCourseRound().getCourseType().isIndividual() || courseSchedule.getCourseRound().getCourseType().isSpecial() || courseSchedule.getCourseRound().getCourseType().isPosture()) && courseSchedule.getCourse().isExperience()) {
            Date dayhour = DateUtils.parseDate(DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM-dd ") + courseSchedule.getOccurHour(), "yyyy-MM-dd HH:mm");
            String restHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(dayhour, 1), "HH:mm");
            CoachRestCalendar calendar = courseScheduleDao.loadCoachRestCalendar(courseSchedule.getCoach(), courseSchedule.getOccurDate(), restHour);
            
            if(calendar == null || calendar.isDuty()) {
                Date newDayHour = DateUtils.parseDate(day + " " + occurHour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                
                CoachRestCalendar newCalendar = new CoachRestCalendar(coach, CoachRestType.EXPERIENCE, occurDate, newRestHour, true);
                genericDaoSupport.save(newCalendar);
            }else if(calendar.isExperience()) {
                Date newDayHour = DateUtils.parseDate(day + " " + occurHour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                
                genericDaoSupport.update(calendar.edit(coach, occurDate, newRestHour));
            } else {
                throw new BusinessException("所选时间不可预约");
            }
        }
        
        if(courseSchedule.getCourseRound().getCourseType().isSpecial() && courseSchedule.getCourse().getCourseUnit().equals(CourseUnit.SPECIAL_UNION)) {
            Date dayhour = DateUtils.parseDate(DateFormatUtils.format(courseSchedule.getOccurDate(), "yyyy-MM-dd ") + courseSchedule.getOccurHour(), "yyyy-MM-dd HH:mm");
            String restHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(dayhour, 1), "HH:mm");
            CoachRestCalendar calendar = courseScheduleDao.loadCoachRestCalendar(courseSchedule.getCoach(), courseSchedule.getOccurDate(), restHour);
            
            if(calendar == null || calendar.isDuty()) {
                Date newDayHour = DateUtils.parseDate(day + " " + occurHour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                
                CoachRestCalendar newCalendar = new CoachRestCalendar(coach, CoachRestType.ANNUAL_LEAVE, occurDate, newRestHour, true);
                genericDaoSupport.save(newCalendar);
            }else if(calendar.getCoachRestType().equals(CoachRestType.ANNUAL_LEAVE)) {
                Date newDayHour = DateUtils.parseDate(day + " " + occurHour, "yyyy-MM-dd HH:mm");
                String newRestHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(newDayHour, 1), "HH:mm");
                
                genericDaoSupport.update(calendar.edit(coach, occurDate, newRestHour));
            } else {
                throw new BusinessException("所选时间不可预约");
            }
        }
        
        scheduleDaoSupport.disableScheduleSmsNotice(courseSchedule);

        genericDaoSupport.update(courseSchedule.editCoach(coach).schedule(day, occurHour).roomScheduleRecord(roomScheduleRecord));

        PrincipalMessage principalMessage = new PrincipalMessage(courseSchedule.getCoach());
        genericDaoSupport.save(principalMessage.initialize("单节课程改课时间", "您有新的单节课程时间", "您有新的单节课程时间已被修改，学员为【"
                + courseSchedule.getStudent().getName() + "】，上课时间为【" + courseSchedule.getDayhour() + "】,请在我的学员点击查看。"));
        
        scheduleDaoSupport.createScheduleSmsNotice(courseSchedule);
        
        scheduleDaoSupport.recalculateStudentCourseware(courseSchedule.getCourseRound());
    }

    /*
     * (non-Javadoc)
     * 
     * @see co.fitstart.admin.service.ecommerce.CourseScheduleService#delete(co.
     * fitstart.common.entity.course.CourseSchedule)
     */
    public void cancel(CourseSchedule courseSchedule) {
        courseScheduleDao.freezeScheduleRecord(courseSchedule.getCourseRound().getCode(), courseSchedule.getOccurDate(),
                courseSchedule.getOccurHour());
        
        if(courseSchedule.getCourseRound().getCourseType().isYear() && courseSchedule.getScheduleStatus().isExpiry()) {
            genericDaoSupport.update(courseSchedule.disabled());
            
            CourseRound courseRound = courseSchedule.getCourseRound();
            if (courseRound.isDishonesty()) {
                if (courseScheduleDao.count(courseRound, ScheduleStatus.EXPIRY) < 2) {
                    genericDaoSupport.update(courseRound.editAttribute("dishonesty", "0"));
                } else {
                    List<ScheduleStatus> scheduleStatus = new ArrayList<>();
                    scheduleStatus.add(ScheduleStatus.EXPIRY);
                    scheduleStatus.add(ScheduleStatus.FINISH);

                    List<CourseSchedule> schedules = courseScheduleDao.listCourseSchedule(courseRound, scheduleStatus, 0, 2);
                    if(schedules.get(0).getScheduleStatus().isFinish() && schedules.get(1).getScheduleStatus().isFinish()) {
                        genericDaoSupport.update(courseRound.editAttribute("dishonesty", "0"));
                    }
                }
            }
        } else {
            PrincipalMessage principalMessage = new PrincipalMessage(courseSchedule.getCoach());
            genericDaoSupport.save(principalMessage.initialize("单节课程取消", "您有新的单节课程被取消", "您有新的单节课程已被取消，学员为【"
                    + courseSchedule.getStudent().getName() + "】，上课时间为【" + courseSchedule.getDayhour() + "】,请在我的学员点击查看。"));
            
            if(courseSchedule.getCourseType().isIndividual() || courseSchedule.getCourseType().isSpecial() || courseSchedule.getCourseType().isPosture()) {
                genericDaoSupport.update(courseSchedule.cancel());
                
                if(courseSchedule.getCourse().isExperience()) {
                    String experienceHour = (Integer.parseInt(courseSchedule.getOccurHour().split(":")[0]) + 1) + ":" + courseSchedule.getOccurHour().split(":")[1];
                    CoachRestCalendar experience = courseScheduleDao.loadCoachRestCalendar(courseSchedule.getCoach(), courseSchedule.getOccurDate(), experienceHour);
                    if(experience != null && experience.isExperience()) {
                        genericDaoSupport.update(experience.disable());
                    }
                }
            } else if(courseSchedule.getCourseType().isYear()) {
                genericDaoSupport.update(courseSchedule.disabled());
            }

            scheduleDaoSupport.disableScheduleSmsNotice(courseSchedule);
        }
        
        scheduleDaoSupport.recalculateStudentCourseware(courseSchedule.getCourseRound());
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#expirySearch(co.fitstart.
     * admin.entity.content.ExpiryScheduleSearch)
     */
    @SuppressWarnings("unchecked")
    public List<CourseSchedule> expirySearch(ExpiryScheduleSearch expiryScheduleSearch) {
        List<CourseSchedule> result = genericDaoSupport.searchForList(expiryScheduleSearch.buildSentenceTemplate(),
                expiryScheduleSearch.buildParameters());

        return result;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#getAvailableScheduleCoach
     * (co.fitstart.entity.site.Site, java.util.Date, java.lang.String,
     * java.util.List)
     */
    public List<Principal> getAvailableScheduleCoach(Site site, Date day, String hour, List<Principal> coaches) {

        List<String> hours = scheduleDaoSupport.getScheduleObligatoryHours(hour);

        List<Principal> onScheduleCoach = scheduleDaoSupport.listOnScheduleCoachIds(day, hours);
        coaches.removeAll(onScheduleCoach);

        List<Principal> onRestCoach = scheduleDaoSupport.listOnRestCoachIds(day, hours.subList(0, 2));
        coaches.removeAll(onRestCoach);

        return coaches;
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#validateCoachTime(co.
     * fitstart.entity.principal.Principal, java.util.Date, java.lang.String)
     */
    public void validateCoachTime(Principal coach, Date date, String hour) {
        Date currentDate = DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd ") + hour, "yyyy-MM-dd HH:mm");
        
        if(currentDate.before(new Date())) {
            throw new BusinessException("所选时间小于当前时间");
        }
        
        String beforeHalfhour = DateFormatUtils
                .format(org.apache.commons.lang3.time.DateUtils.addMinutes(currentDate, -30), "HH:mm");
        String afterHalfhour = DateFormatUtils
                .format(org.apache.commons.lang3.time.DateUtils.addMinutes(currentDate, 30), "HH:mm");
        String afterHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(currentDate, 1),
                "HH:mm");

        List<CourseSchedule> schedules = courseScheduleDao.listCourseSchedule(coach, currentDate,
                new String[] { beforeHalfhour, hour, afterHalfhour, afterHour });
        if (!schedules.isEmpty()) {
            throw new BusinessException("该时间此教练已有课程");
        }

        List<CoachRestCalendar> calendars = coachRestCalendarDao.list(coach, currentDate,
                new String[] { hour, afterHalfhour, afterHour });
        if (!calendars.isEmpty()) {
            throw new BusinessException("该时间此教练休息");
        }
    }

    /*
     * (non-Javadoc)
     * 
     * @see
     * co.fitstart.admin.service.CourseScheduleService#validateStudentTime(co.
     * fitstart.entity.user.Student, java.util.Date, java.lang.String)
     */
    public void validateStudentTime(String mobile, Date date, String hour) {

        Student student = studentDao.load(mobile);
        if (student != null) {
            Date currentDate = DateUtils.parseDate(DateFormatUtils.format(date, "yyyy-MM-dd ") + hour,
                    "yyyy-MM-dd HH:mm");
            String beforeHour = DateFormatUtils
                    .format(org.apache.commons.lang3.time.DateUtils.addHours(currentDate, -1), "HH:mm");
            String beforeHalfhour = DateFormatUtils
                    .format(org.apache.commons.lang3.time.DateUtils.addMinutes(currentDate, -30), "HH:mm");
            String afterHalfhour = DateFormatUtils
                    .format(org.apache.commons.lang3.time.DateUtils.addMinutes(currentDate, 30), "HH:mm");
            String afterHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(currentDate, 1),
                    "HH:mm");

            List<CourseSchedule> schedules = courseScheduleDao.listCourseSchedule(student, date,
                    new String[] { beforeHalfhour, hour, afterHalfhour, afterHour }, false);
            List<CourseSchedule> experienceSchedules = courseScheduleDao.listCourseSchedule(student, date,
                    new String[] { beforeHour, beforeHalfhour, hour, afterHalfhour, afterHour }, true);
            if (!schedules.isEmpty() || !experienceSchedules.isEmpty()) {
                throw new BusinessException("该时间此学员已有课程");
            }
        }
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#listAvailableExperienceHour(java.lang.Long, java.util.Date, co.fitstart.entity.site.Site)
     */
    public List<Map<String, String>> listAvailableExperienceHour(Long coachId, Date date, Site site) {
        List<Map<String, String>> result = new ArrayList<>();
        
        List<String> freeScheduleTimes = scheduleDaoSupport.listFreeScheduleTime(coachId, date, site, CoachRestType.EXPERIENCE);
        
        for(String freeScheduleTime : freeScheduleTimes) {
            if(!DateUtils.isSameDay(date, new Date()) || compareHour(freeScheduleTime)) {
                Map<String, String> map = new HashMap<>();
                map.put("begin", freeScheduleTime);
                map.put("end", getExpEndTime(freeScheduleTime));
                
                result.add(map);
            }
        }
        return result;
    }
    
    /**
     * 
     * @param time
     * @return
     */
    private String getExpEndTime(String time) {
        String[] arr = time.split(":");
        if(arr[1].equalsIgnoreCase("00")) {
            int hour = Integer.parseInt(arr[0]) + 1;
            return Integer.toString(hour) + ":30";
        } else {
            int hour = Integer.parseInt(arr[0]) + 2;
            return Integer.toString(hour) + ":00";
        }
    }
    
    /**
     * 
     * @param dayhour
     * @return
     */
    private boolean compareHour(String dayhour) {
        String[] dayhours = dayhour.split(":");
        
        Calendar now = Calendar.getInstance();
        now.setTime(new Date());
        int hour = now.get(Calendar.HOUR_OF_DAY);
        int minute = now.get(Calendar.MINUTE);
        
        if (Integer.parseInt(dayhours[0]) > hour) {
            return true;
        } else if (Integer.parseInt(dayhours[0]) == hour && Integer.parseInt(dayhours[1]) >= minute) {
            return true;
        }
        return false;
    }
    
    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#getAvailableRoomType(co.fitstart.entity.site.Site, java.util.Date, java.lang.String, boolean)
     */
    public RoomType getAvailableRoomType(Site site, Date day, String hour, boolean experience) {
    	logger.info("#1 room 店名 [{}] 总私密房间 [{}] 可用私密房间[{}]",site.getName(),site.getLimit(RoomType.ROOM),this.getRemainRooms(site, day, hour,  RoomType.ROOM));
    	logger.info("#1 common 店名 [{}] 总公共房间 [{}] 可用公共房间[{}]",site.getName(),site.getLimit(RoomType.COMMON),this.getRemainRooms(site, day, hour, RoomType.COMMON));
        if(site.getLimit(RoomType.ROOM) > 0 && this.getRemainRooms(site, day, hour, RoomType.ROOM) > 0) {
            return RoomType.ROOM;
        } else if(site.getLimit(RoomType.COMMON) > 0 && this.getRemainRooms(site, day, hour,  RoomType.COMMON) > 0) {
            return RoomType.COMMON;
        } else {
            throw new BusinessException("无可用区域");
        }
    }
    
    /*
     */
//    public RoomType getAvailableRoomType(Site site, Date day, String hour, boolean experience, CourseSchedule courseSchedule) {
//    	logger.info("#2 room 店名 [{}] 总私密房间 [{}] 可用私密房间[{}]",site.getName(),site.getLimit(RoomType.ROOM),this.countAvailableRoom(site, day, hour, experience, RoomType.ROOM, courseSchedule));
//    	logger.info("#2 common 店名 [{}] 总公共房间 [{}] 可用公共房间[{}]",site.getName(),site.getLimit(RoomType.COMMON),this.countAvailableRoom(site, day, hour, experience, RoomType.COMMON, courseSchedule));
//        
//        if(site.getLimit(RoomType.ROOM) > 0 && this.countAvailableRoom(site, day, hour, experience, RoomType.ROOM, courseSchedule) > 0) {
//            return RoomType.ROOM;
//        } else if(site.getLimit(RoomType.COMMON) > 0 && this.countAvailableRoom(site, day, hour, experience, RoomType.COMMON, courseSchedule) > 0) {
//            return RoomType.COMMON;
//        } else {
//            throw new BusinessException("无可用区域");
//        }
//    }
    
    
    public RoomType getAvailableRoomType(Site site, Date day, String hour, boolean experience, CourseSchedule courseSchedule) {
    	logger.info("#2 room 店名 [{}] 总私密房间 [{}] 可用私密房间[{}]",site.getName(),site.getLimit(RoomType.ROOM),this.getRemainRooms(site, day, hour,  RoomType.ROOM));
    	logger.info("#2 common 店名 [{}] 总公共房间 [{}] 可用公共房间[{}]",site.getName(),site.getLimit(RoomType.COMMON),this.getRemainRooms(site, day, hour, RoomType.COMMON));
        
        if(site.getLimit(RoomType.ROOM) > 0 && this.getRemainRooms(site, day, hour, RoomType.ROOM) > 0) {
            return RoomType.ROOM;
        } else if(site.getLimit(RoomType.COMMON) > 0 && this.getRemainRooms(site, day, hour,  RoomType.COMMON) > 0) {
            return RoomType.COMMON;
        } else {
            throw new BusinessException("无可用区域");
        }
    }    
    
    /**
     * @author LZJ
     * @param site
     * @param day
     * @param hour
     * @param roomType
     * @return
     */
    private int getRemainRooms(Site site, Date day, String hour, RoomType roomType) {
    	
    	String appointDay = DateFormatUtils.format(day, "yyyy-MM-dd ");
    	
    	Date appointHour = DateUtils.parseDate(appointDay + hour,
                "yyyy-MM-dd HH:mm");
    	
    	//1.所约课时间1小时前后 (体验课占用房间时间)	
    	String beforeHour = DateFormatUtils
                 .format(org.apache.commons.lang3.time.DateUtils.addHours(appointHour, -1), "HH:mm");
    	
//    	String afterHour = DateFormatUtils
//                .format(org.apache.commons.lang3.time.DateUtils.addHours(appointHour, 1), "HH:mm");    	
    	
    	//2.所约课时间半小时前 
    	String beforeHalfHour = DateFormatUtils
                 .format(org.apache.commons.lang3.time.DateUtils.addMinutes(appointHour, -30), "HH:mm");
    	 
    	//3.所约课时间半小时后    	
//    	String afterHalfHour = DateFormatUtils
//                .format(org.apache.commons.lang3.time.DateUtils.addMinutes(appointHour, 30), "HH:mm");
    	
    	//普通课占用房间数
    	int currentUsingCount = scheduleDaoSupport.getUsingRoom(site, day, beforeHalfHour, hour, roomType);
    	
    	//预约课占用的房间数    	
    	int experienceUsingCount = scheduleDaoSupport.getUsingRoom(site, day, beforeHour, hour);
    	
    	logger.info("LZJ get data currentUsingCount [{}] experienceUsingCount [{}]",currentUsingCount,experienceUsingCount);    	    	
    	
    	return site.getLimit(roomType) - (currentUsingCount+experienceUsingCount) ;
    }
    
    @Override
    public List<Principal> getAllCoachs() {
    	
    	List<PrincipalReportRelation> principalReportRelations = courseScheduleDao.listAllRelation();
    	List <Long> coachIds = new ArrayList<>();
    	for ( PrincipalReportRelation principalReportRelation : principalReportRelations) {
			if(principalReportRelation.getCoach() != null) {
				Long id = principalReportRelation.getCoach().getId();
				coachIds.add(id);
			}
		}
    	
    	List<Principal> coachs = courseScheduleDao.getSiteCoachs(coachIds);
		return coachs;
    }

    /*
     * (non-Javadoc)
     * @see co.fitstart.admin.service.CourseScheduleService#getSiteCoachs(co.fitstart.entity.site.Site)
     */
	public List<Principal> getSiteCoachs(Site site) {
		
		List<PrincipalReportRelation> principalReportRelations = courseScheduleDao.listSiteCoachIds(site);
		
		List <Long> coachIds = new ArrayList<>();
		
		for ( PrincipalReportRelation principalReportRelation : principalReportRelations) {
			
			if(principalReportRelation.getCoach() != null) {
				
				Long id = principalReportRelation.getCoach().getId();
				
				coachIds.add(id);
			}
			
			
		}
		
		List<Principal> coachs = courseScheduleDao.getSiteCoachs(coachIds);
		
		
		return coachs;
	}
    
    
}
